// Copyright (C) Calum Grant 2008

#ifndef DYNAMIC_EXTENSIONS_HPP
#define DYNAMIC_EXTENSIONS_HPP

#include "dynamic.hpp"

#include <map>
#include <vector>
#include <typeinfo>

#include "heap.hpp"
#include "gc.hpp"
#include "pickler.hpp"
#include "unpickler.hpp"
#include "nonroot_var.hpp"


namespace dynamic
{
	/// Used to create a new item on the stack, for later retrieval by stack_trace().
	class DYNAMIC_API stack_trace_entry : cg::not_assignable, cg::not_copyable
	{
		stack_trace_entry * m_previous;
		var m_fn;
	public:
		stack_trace_entry(const var &fn);
		~stack_trace_entry();
		static var stack_trace();
	};


	/// The base class for all shared objects.
	class DYNAMIC_API shared_var_impl : public gc::gc_object, public var_impl
	{
	public:
		var clone();
		var_cmp_result compare2(const var & other);
		void copy_to(void*) const;
		var proxy();
		void mark_reachable(gc::garbage_collector &);
	};


	/// Helper class to implement var_impl.
	template<typename T>
	class inline_var_impl : public var_impl
	{
	public:
		void copy_to(void * p) const
		{
			cg::staticassert< sizeof(T) <= sizeof(var_variant) >();
			new(p) T( *static_cast<const T*>(this) );
		}
	};


	namespace internal
	{
		/// Contains a C++ object of type B, or any object derived from B.
		template<typename B> class container_base : public shared_var_impl
		{
		public:
			/// Retrieves the C++ object by base class.
			virtual B & get() =0;
		};

		/// Contains a C++ object of type T, with an optional base class B.
		template<typename T, typename B=T>
		class container_impl : public container_base<B>, cg::not_assignable, cg::not_copyable
		{
			T m_data;	/// The contained data
		public:
			container_impl(const T & t) : m_data(t) { }
			container_impl() { }

			T & get() { return m_data; }
			void output(ostream & ss) { ss << typeid(T).name(); }

			void output(wostream & ss) 
			{ 
				for(const char * s = typeid(T).name(); *s; ++s)
					ss << wchar_t(*s);	// Yay for dumb iostreams
			}

			std::string class_name() { return "native"; }
			var_cmp_index comparison_index() { return cmp_native; }
			void mark_children(gc::garbage_collector &) { }
		};

		template<typename Derived,typename DerefType> template<typename T>
		T & var_methods<Derived,DerefType>::as()
		{ 
			internal::container_base<T> * p = dynamic_cast< internal::container_base<T>* >(deref().impl().shared_var());

			if(p) 
				return p->get();
			else
				throw not_supported(deref(), "as");
		}

	}

	/// Creates a shared object wrappering a C++ object.
	template<typename T>
	var create(const T & t)
	{
		return new dynamic::internal::container_impl<T>(t);
	}

	/// Creates a shared object wrappering a C++ object - default constructor.
	template<typename T>
	var create()
	{
		return new dynamic::internal::container_impl<T>();
	}

	/// Creates a shared object wrapping a C++ object.
	template<typename B, typename T>
	var create_base(const T & t)
	{
		return new dynamic::internal::container_impl<T,B>(t);
	}

	template<typename B, typename T>
	var create_base()
	{
		return new dynamic::internal::container_impl<T,B>();
	}

	template<typename B, typename T>
	var create_ref(T & t)
	{
		return new dynamic::internal::container_impl<T&,B>(t);
	}

	template<typename B, typename T>
	T & as_derived(const var & v)
	{
		return dynamic_cast<internal::container_impl<T,B>&>(*v.impl().shared_var()).get();
	}
}

#endif
